/* This file is part of SpaceSim.
 
 SpaceSim is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.
 
 SpaceSim is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with SpaceSim.  If not, see <http://www.gnu.org/licenses/>. */

#include <cmath>
#include <sstream>

#include "graphics.h"
#include "planet.h"
#include "time.h"

template <class T>
inline std::string toString (const T& t) {
	std::stringstream str;
	str << t;
	return str.str();
}

std::string PlanetType::GetTypeString(int type) {
	switch (type) {
		case PlanetType::Rocky:
			return "Rocky";
			break;
		case PlanetType::Quartz:
			return "Quartz";
			break;
		case PlanetType::Martian:
			return "Martian";
			break;
		case PlanetType::Atmos:
			return "Atmos";
			break;
		case PlanetType::Ice:
			return "Ice";
			break;
		case PlanetType::Water:
			return "Water";
			break;
		case PlanetType::Habitable:
			return "Habitable";
			break;
		case PlanetType::Venusian:
			return "Venusian";
			break;
		case PlanetType::GasGiant:
			return "Gas Giant";
			break;
		case PlanetType::WarpLauncher:
			return "Warp Launcher";
			break;
		case PlanetType::HubLauncher:
			return "Hub Launcher";
			break;	
		case PlanetType::FreeStation:
			return "Station";
			break;	
		case PlanetType::NoType:
		default:
			return "Planet";
			break;
	}
}

Planet::Planet() {
	Moons.clear();
	ORadius = 0.0;
	Mass = 0.0;
	Parent = NULL;
	OPeriod = 0.0;
	MoonCount = 0;
}

Planet::Planet(float _radius, float _mass, float _starmass, Planet *_parent, PlanetType::Enum _type, float _temp, float _atm) {	// Constructor
	Moons.clear();
	ORadius = _radius;
	Mass = _mass;
	StarMass = _mass;
	Parent = _parent;
	float RealRadius = _radius * 1e8;
	if (Parent == NULL) {
		RecalculateOrbit();
	} else {
		RecalculateOrbit(Parent->GetMass());
	}
	Type = _type;
	MoonCount = 0;
	Temperature = _temp;
	Atmosphere = _atm;
}

void Planet::SetOrbitRadius(float _radius) {
	ORadius = _radius;
	float RealRadius = _radius * 1e8;
	if (Parent == NULL) {
		RecalculateOrbit();
	} else {
		RecalculateOrbit(Parent->GetMass());
	}
}

void Planet::Update() {
	ROtoXY();
}

void Planet::Draw() {
	Draw(3.0);
}

void Planet::Draw(float radius) {
	if (Parent == NULL) {
		Graphics::App.Draw(sf::Shape::Circle(0.0, 0.0, ORadius * Graphics::ZoomFactor, sf::Color(0.0, 0.0, 0.0, 0.0), 1.0, sf::Color(128.0, 128.0, 128.0, 255.0)));	// draw the orbit
		Graphics::App.Draw(sf::Shape::Circle(XPos * Graphics::ZoomFactor, YPos * Graphics::ZoomFactor, radius, sf::Color::Blue));	// draw!
		sf::String typeString(PlanetType::GetTypeString(Type));
		typeString.SetSize(15);
		typeString.SetPosition(XPos * Graphics::ZoomFactor, YPos * Graphics::ZoomFactor);
		Graphics::App.Draw(typeString);
		for (std::vector<Planet>::iterator moonIterator=Moons.begin(); moonIterator < Moons.end(); moonIterator++) {
			moonIterator->Update();
			moonIterator->Draw(2.0);
		}
	} else {
		Graphics::App.Draw(sf::Shape::Circle(Parent->GetX() * Graphics::ZoomFactor, Parent->GetY()  * Graphics::ZoomFactor, ORadius  * Graphics::ZoomFactor, sf::Color(0.0, 0.0, 0.0, 0.0), 1.0, sf::Color(128.0, 128.0, 128.0, 255.0)));	// draw the orbit
		Graphics::App.Draw(sf::Shape::Circle((Parent->GetX() + XPos) * Graphics::ZoomFactor, (Parent->GetY() + YPos) * Graphics::ZoomFactor, radius, sf::Color::White));	// draw!
		sf::String typeString(PlanetType::GetTypeString(Type));
		typeString.SetSize(15);
		typeString.SetPosition((Parent->GetX() + XPos) * Graphics::ZoomFactor, (Parent->GetY()+YPos) * Graphics::ZoomFactor);
		//Graphics::App.Draw(typeString);
	}
}

void Planet::DrawInfo(int number) {
	Update();
	if (Parent == NULL) {
		sf::String infoString(toString(number) + " " + toString(GetOrbitRadius()) + " " + toString(GetOrbitFraction()) + " " + PlanetType::GetTypeString(Type) + " Moons: " + toString(GetMoonCount()) + + ", " + toString(Temperature) + "K, " + toString(Atmosphere) + "atm");
		infoString.SetSize(15);
		infoString.SetPosition(30, number * 30 + 20);
		Graphics::App.Draw(infoString);
	}
}

bool IsOrbitRadiusSmaller (Planet a, Planet b) {
	return a.GetOrbitRadius() < b.GetOrbitRadius();
}

void Planet::DrawStation() {
	Update();
	if (Parent == NULL) {
		sf::String infoString ("This is definitely a planet. Yup, it certainly is.");
		if (Type == PlanetType::FreeStation) {
			infoString.SetText("You are on a local Exchange and Defence Station. (T=" + toString(Temperature) + "K, P=" + toString(Atmosphere) + "atm)");
		} else {
			infoString.SetText("You are on the Exchange Station for a " + PlanetType::GetTypeString(Type) + "-type planet. (T=" + toString(Temperature) + "K, P=" + toString(Atmosphere) + "atm)");
		}
		infoString.SetSize(15);
		infoString.SetPosition(20, 50);
		Graphics::App.Draw(infoString);
	}
}

float MakeTemperature(PlanetType::Enum type, float radius, float atmosphere) {
	float albedo = 0.15;
	float greenhouse = 0.0;
	
	switch (type) {
		case PlanetType::Rocky:
			albedo = 0.15;
			greenhouse = 0.0;
			break;
		case PlanetType::Quartz:
			albedo = 0.25;
			greenhouse = 0.0;
			break;
		case PlanetType::Martian:
			albedo = 0.20;
			greenhouse = 10.0;
			break;
		case PlanetType::Atmos:
			albedo = 0.40;
			greenhouse = 20.0;
			break;
		case PlanetType::Ice:
			albedo = 0.60;
			greenhouse = 20.0;
			break;
		case PlanetType::Water:
			albedo = 0.50;
			greenhouse = 20.0;
			break;
		case PlanetType::Habitable:
			albedo = 0.60;
			greenhouse = 20.0;
			break;
		case PlanetType::Venusian:
			albedo = 0.70;
			greenhouse = 500.0;
			break;
		case PlanetType::GasGiant:
			albedo = 0.40;
			greenhouse = 0.0;
			break;	
		case PlanetType::NoType:
		default:
			albedo = 0.20;
			greenhouse = 0.0;
			break;
	}
	
	return 6000 * sqrt(7 * sqrt(1 - albedo) / (2 * radius)) + greenhouse;
}